/**
 * 
 */
package fr.gdi.android.news;

import java.io.File;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.content.ContentValues;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.text.TextUtils;
import android.util.Log;
import fr.gdi.android.news.DatabaseHelper.Query;
import fr.gdi.android.news.parser.Feed;
import fr.gdi.android.news.parser.Item;
import fr.gdi.android.news.utils.AsyncImageLoader;
import fr.gdi.android.news.utils.Constants;
import fr.gdi.android.news.utils.Constants.Configuration;
import fr.gdi.android.news.utils.Constants.Preferences;
import fr.gdi.android.news.utils.Constants.Provider;
import fr.gdi.android.news.utils.Constants.URI;
import fr.gdi.android.news.utils.FeedUtils;
import fr.gdi.android.news.utils.IOUtils;
import fr.gdi.android.news.utils.PreferenceUtils;
import fr.gdi.android.news.utils.Utils;
import fr.gdi.android.news.widgets.ScrollableNewsWidget;

final class FetchTask extends AsyncTask<Object, Void, Void>
{
    public static SimpleDateFormat PURL_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");

    private static final List<String> UNKNOWN_AUTHOR = new ArrayList<String>(Arrays.asList(new String[] { "(author unknown)" } ));
    
    private static final Pattern IMG_PATTERN = Pattern.compile("<img[^>]+>", Pattern.CASE_INSENSITIVE), IMG_SRC_ATTR_PATTERN = Pattern.compile(
            "src=('|\")([^('|\")]*)('|\")", Pattern.CASE_INSENSITIVE);

    private Context ctx = null;
    private Integer[] appWidgetIds = null;
    
    
    private List<String> thumbnails = new ArrayList<String>();
    
    @Override
    protected Void doInBackground(Object... args)
    {
        ctx = (Context) args[0];
        
        
        Integer appWidgetId = (Integer) args[1];
        Uri uri = (Uri) args[2];
        
        if (appWidgetId == null)
        {
            final int[] tmpWidgetIds = Utils.getAllWidgetIds(ctx);
            appWidgetIds = new Integer[tmpWidgetIds.length];
            for (int i = 0; i < tmpWidgetIds.length; i++)
            {
                appWidgetIds[i] = tmpWidgetIds[i];
            }
        }
        else
        {
            appWidgetIds = new Integer[1];
            appWidgetIds[0] = appWidgetId.intValue();
        }
        
        if (Utils.isOnline(ctx)) 
        {
            Map<FeedDescription, Boolean> fetchThumbails = new HashMap<FeedDescription, Boolean>();
            
            ScrollableNewsWidget.setLoading(ctx, appWidgetIds, true);
            
            Set<FeedDescription> feeds = new TreeSet<FeedDescription>();
            for (int i : appWidgetIds)
            {
                List<FeedDescription> wf = FeedUtils.getFeedDescriptions(ctx, i);
                boolean showImages = PreferenceUtils.getBooleanPreference(ctx, Preferences.SHOW_IMAGES, appWidgetId);
                for (FeedDescription f : wf)
                {
                    feeds.add(f);
                    if ( showImages ) 
                    {
                        fetchThumbails.put(f, true);
                    }
                }
            }
            
            load(ctx, feeds, uri, fetchThumbails, new Runnable() {
                public void run() {
                    long date = new Date().getTime();
                    for (int id : appWidgetIds)
                    {
                        PreferenceUtils.setLong(ctx, Preferences.LAST_UPDATE + id, date);
                        Uri widgetUri = Constants.URI.CONTENT_URI_WIDGET_FEEDS.buildUpon().appendEncodedPath(Integer.toString(id)).build();
                        ctx.getContentResolver().notifyChange(widgetUri, null);
                    }                    
                }
            });
        }
        
        return null;
    }
    
    private String getImageUrl(Item entry)
    {
        //if ( entry.getImage() != null ) return entry.getImage().toString();
        
        String desc = entry.getContent() == null ? "(no summary)" : entry.getContent();
        
        try
        {
            //crap... should we? ==> add option PARSE_SUMMARY
            Matcher m = IMG_PATTERN.matcher(desc);
            if (m.find())
            {
                Matcher m2 = IMG_SRC_ATTR_PATTERN.matcher(m.group());
                if (m2.find())
                {
                    String attr = m2.group();
                    String uri = attr.substring(5, attr.length() - 1);
                    
                    if (Constants.USE_EMULATOR)
                    {
                        Log.d(this.getClass().getName(), "Found news thumbnail: " + uri);
                    }
                    
                    return uri;
                }
            }
            return null;
        }
        catch (Exception e)
        {
            Log.e(this.getClass().getName(), "Unable to extract thumbnail url", e);
            return null;
        }
    }
    
    private void load(Context ctx, Set<FeedDescription> feeds, Uri uri, Map<FeedDescription, Boolean> fetchThumbnails, Runnable afterLoad)
    {
        Map<Item, Boolean> fetchItemThumbnails = new HashMap<Item, Boolean>();
        List<Item> allEntries = new ArrayList<Item>();
        List<String> names = new ArrayList<String>();
        int v = 0;
        for (FeedDescription f : feeds)
        {
            v++;

            Feed feed = FeedUtils.getMostRecentNews(ctx, f);
            
            if ( feed == null )
            {
                Log.w(this.getClass().getName(), "Feed " + f.url + " is null. This might indicate a configuration problem. Trying to gracefully bypass this issue.");
                if ( allEntries.size() == 0 && v == feeds.size() )
                {
                    //no entry has been collected and this is our last chance to warn the user about a potential issue
                    feed = new Feed();
                    feed.setTitle(ctx.getText(R.string.feed_not_found).toString());
                    Item item = new Item();
                    item.setContent(ctx.getText(R.string.feed_not_found_item_content).toString());
                    item.setDescription(ctx.getText(R.string.feed_not_found_item_content).toString());
                    item.setPubdate(new Date());
                    item.setAuthor(ctx.getText(R.string.feed_not_found_item_author).toString());
                }
                else 
                {
                    continue;
                }
            }
           
            List<Item> items = feed.getItems();
            if (feed != null && items.size() > 0)
            {
                Boolean fetchImage = fetchThumbnails.get(f);
                names.add(f.name);
                String title = TextUtils.isEmpty(feed.getTitle()) ? f.name : feed.getTitle();
                for (Item e : items)
                {
                    feed.setTitle(f.name);
                    if ( fetchImage != null ) fetchItemThumbnails.put(e, fetchImage); 
                    e.setSource(feed);
                    if ( PreferenceUtils.getBooleanPreference(ctx, Preferences.USE_REAL_AUTHOR_NAME) && !TextUtils.isEmpty(e.getOriginalAuthor()) && !UNKNOWN_AUTHOR.contains(e.getOriginalAuthor().toLowerCase()) )
                    {
                        e.setAuthor(e.getOriginalAuthor());
                    }
                    else 
                    {
                        e.setAuthor(TextUtils.isEmpty(e.getOriginalSource()) ? title : e.getOriginalSource());
                    }
                    allEntries.add(e);
                }
            }
        }
        
        DatabaseHelper.lock();
        SQLiteDatabase db = null;
        
        try
        {
            DatabaseHelper helper = new DatabaseHelper(ctx);
            db = helper.getWritableDatabase();
            
            deleteOldRows(helper, db, names);
            
            db.beginTransaction();
            for (int u = 0; u < allEntries.size(); u++)
            {
                Item entry = allEntries.get(u);
                
                ContentValues values = new ContentValues();
                
                String feed = entry.getSource().getTitle();
                //getContents 
                //getEnclosures
                
                //encoding
                values.put(Provider.Columns.NEWS_LINK.toString(), entry.getLink() != null ? entry.getLink().toString() : "");
                values.put(Provider.Columns.NEWS_TITLE.toString(), entry.getTitle());
                //try to fetch original source. defaults to registered feed name (see FeedUtils#getMostRecentNews)
                String author = entry.getAuthor();
                
                values.put(Provider.Columns.NEWS_AUTHOR.toString(), author);

                values.put(Provider.Columns.FEED_NAME.toString(), feed);
                
                String date = DatabaseHelper.SQL_DATE_FORMAT_WITH_YEAR.format(getDate(entry));
                values.put(Provider.Columns.NEWS_DATE.toString(), date);
                
                String sd = entry.getContent();
                String desc = TextUtils.isEmpty(sd) ? "(no summary)" : sd;
               
                String thumbnail = entry.getImage() != null ? entry.getImage().toString() : null;
                String priority = PreferenceUtils.getStringPreference(ctx, Preferences.THUMB_PRIORITY, null, "ENCLOSURE");
                if ( (priority == Configuration.SUMMARY || TextUtils.isEmpty(thumbnail)) && !PreferenceUtils.getBooleanPreference(ctx, Preferences.PARSE_DESCRIPTION) )
                {
                    thumbnail = getImageUrl(entry);
                }
                //##
                //thumbnail = "http://gdodinet.free.fr/image.php?r=" + new Random(435311991009L).nextInt();
                //##
                desc = Utils.stripTags(desc);
                
                Boolean fetchImage = fetchItemThumbnails.get(entry);
 
                if (!TextUtils.isEmpty(thumbnail) && fetchImage != null && fetchImage) 
                {
                    File fullImage = IOUtils.getImageCacheFile(thumbnail, "full");
                    if ( !fullImage.exists() )
                    {
                        thumbnails.add(thumbnail.replaceAll("&amp;", "&"));
                    }
                }
                
                values.put(Provider.Columns.NEWS_DESCRIPTION.toString(), desc);
                values.put(Provider.Columns.NEWS_IMAGE_URL.toString(), thumbnail);
                
                db.insert(DatabaseHelper.NEWS_TABLE, null, values);
            }
            db.setTransactionSuccessful();
        }
        catch (SQLException e)
        {
            // ?
        }
        finally
        {
            db.endTransaction();
            db.close();
            DatabaseHelper.unlock();
            afterLoad.run();
        }
    }
    
    private Date getDate(Item e)
    {
        return e.getDate();
    }
    
    private void deleteOldRows(DatabaseHelper helper, SQLiteDatabase db, List<String> names)
    {
        
        db.beginTransaction();
        try
        {
            Query query = helper.getDeleteQuery(names);
            
            if (Constants.USE_EMULATOR)
            {
                Log.d(this.getClass().getName(), "Deleting old rows. Query= " + query.sql + "; feeds= " + names);
            }
            
            db.execSQL(query.sql, query.params);
            db.setTransactionSuccessful();
        }
        finally
        {
            db.endTransaction();
        }
    }
    
    protected void onPostExecute(Void v)
    {
        ScrollableNewsWidget.setLoading(ctx, appWidgetIds, false);
        
        updateUI();
        
        final Set<String> thumbs = new TreeSet<String>();
        
        thumbs.addAll(thumbnails);
        
        final int size = thumbs.size();
        if (size > 0)
        {
            AsyncImageLoader.ImageLoadedListener listener = new AsyncImageLoader.ImageLoadedListener() {
                int count = size;
                
                @Override
                public void imageLoaded(Bitmap imageBitmap)
                {
                    count--;
                    if (count == 0)
                    {
                        ScrollableNewsWidget.setDownloading(ctx, appWidgetIds, false);
                        for (Integer i : appWidgetIds)
                        {
                            //ScrollableNewsWidget.redraw(ctx, i);
                            Uri widgetUri = URI.CONTENT_URI_WIDGET_FEEDS.buildUpon().appendEncodedPath(Integer.toString(i)).build();
                            ctx.getContentResolver().notifyChange(widgetUri, null);
                        }
                    }
                }
            };
            
            ScrollableNewsWidget.setDownloading(ctx, appWidgetIds, true);
            new AsyncImageLoader(ctx).loadImage(thumbs, listener);
        }
    }
    
    private void updateUI()
    {
        if (appWidgetIds != null)
        {
            for (Integer i : appWidgetIds)
            {
                Uri widgetUri = URI.CONTENT_URI_WIDGET_FEEDS.buildUpon().appendEncodedPath(Integer.toString(i)).build();
                ctx.getContentResolver().notifyChange(widgetUri, null);
            }
        }
    }
}